
import

	httparser,
	
	tango.core.Memory,
	tango.core.Array ,
	tango.core.Thread ,
	tango.core.Traits,
	
	tango.io.Stdout ,
	tango.io.device.Array ,
	tango.io.device.File ,
	
	tango.net.device.Berkeley,
	tango.net.InternetAddress,
	tango.net.InternetAddress,
	
	tango.time.Clock,
	
	tango.stdc.stringz,
	
	Int	= tango.text.convert.Integer ,
	libeventd.Types ;

void main(){
	WSADATA wd = void;
	WSAStartup (0x0202, &wd);
	evthread_use_windows_threads();
	auto base	= event_base_new() ;
	//int ret = event_base_start_iocp(base) ; assert(ret is 0) ;
	
	Dns.Init(base);
	auto el	= Http.Init("127.0.0.1", 8888 , base );

	timeval tv	= {999999,0} ;
	event_base_loopexit(base, &tv);
	event_base_dispatch(base);
}

struct Dns {
	alias Dns* 			This ;
	static event_base*	_base ;
	static evdns_base*	_dns ;
	
	static void Init(event_base* base ) {
		assert(base !is null ) ;
		_base	= base ;
		_dns		= evdns_base_new(_base , 0 );
		assert(_dns !is null ) ;
		// evdns_set_log_fn(&logfn);
		int ret	= evdns_base_config_windows_nameservers(_dns);
		assert(ret is 0) ;
	}
	
	static extern(C)	void logfn(int is_warn,  char *msg) {
		Stdout.formatln("{},{}", is_warn, fromStringz(msg) );
	}
	
	static extern(C)	void Server_CallBack(int result, char type, int count, int ttl, void *addresses, void *ctx) {
		This _self		= cast(This) ctx ;
		_self._ts		= Clock.now.unix.seconds ;
		if( result !is 0 ){
			Stdout.formatln("dns query failure result:{}: type:{}", result, type);
			return ;
		}
		_self._ret	= result ;
		_self._stat	= 2 ; 
		if( _self._ret is 0 ) {
			_self._ttl		= ttl ;
			_self._ip		= new int[ count ] ;
			memcpy(&_self._ip[0], addresses,  count * 4  ) ;
		}
		assert( _self._dg !is null ) ;
		foreach( _dg; _self._dg) {
			assert( _dg !is null ) ;
			_dg( _self ) ;
		}
		Stdout.formatln("{}: {}ms ttl:{}", _self._dn,  ( Clock.now - _self._now) . millis, _self._ttl ).flush ;
		while( _self._dg.length > 0 ) {
			auto _dg	= _self._dg[0] ; 
			assert(_dg !is null  && _dg.ptr !is null && _dg.funcptr !is null);
			_dg( _self );
			_self._dg	= _self._dg[ 1 .. $];
		}
	}
	
	static This[]	list = [] ;
	
	alias void delegate(This) Dg_Type ;
	char[]	_dn	;
	Dg_Type	_dg[] ;
	int		_stat	;
	int		_ret	;
	long		_ts	;
	int		_ttl	;
	int[]		_ip	;
	Time		_now ;
	
	
	static void Query(char[] dn , Dg_Type dg) {
		This ptr	= null ;
		foreach( _dns; list) {
			if( dn == _dns._dn[0..$-1] ) {
				ptr	= _dns ;
				break ;
			}
		}
		if(  ptr is null ) {
			ptr		= new Dns ;
			ptr._dn	= new char[ dn.length + 1 ] ;
			memcpy(&(ptr._dn)[0], &dn[0], dn.length );
			ptr._dn[$-1]	= 0 ;
			list		~= ptr ;
		}
		long ts	=  Clock.now.unix.seconds ;
		if( ts - ptr._ts <= ptr._ttl ) {
			dg(ptr);
		} else {
			ptr._now	= Clock.now ;
			ptr._dg	~= dg ;
			if( ptr._stat !is 1 ){
				ptr._stat	= 1 ;
				evdns_base_resolve_ipv4(_dns, ptr._dn.ptr , 0, &Server_CallBack, ptr);
			}
		}
	}
}

struct Http {
	alias Http*		This ;
	event_base*	_base ;
	evconnlistener*	_listener ;
	Address		_addr ;
	Berkeley		_bs ;
	
	static This Init(char[] ip , ushort port , event_base* base ) {
		assert(base !is null ) ;
		This		p	= new Http ;
		p._addr		= new InternetAddress(ip, port);
		p._base		= base ;
		p._bs.open(AddressFamily.INET, SocketType.STREAM, ProtocolType.TCP);
		p._bs.bind( p._addr );
		p._bs.blocking(false);
		
		// LEV_OPT_CLOSE_ON_FREE|LEV_OPT_REUSEABLE|LEV_OPT_CLOSE_ON_EXEC
		p._listener	= evconnlistener_new(p._base, &Accept_Cb,  p, LEV_OPT_CLOSE_ON_FREE|LEV_OPT_REUSEABLE, 32 , p._bs.handle) ;
		Stdout.formatln("http://{}", p._addr.toString ).flush ;
		return p ;
	}
	
	static extern(C) void Accept_Cb(evconnlistener* listener, evutil_socket_t fd, sockaddr* address, int len, void* ctx){
		This _self		= cast(This) ctx ;
		auto req		= Serverlet.Init(_self._base, fd, address );
		req.service() ;
	}
}

template IReq(T) {
	event_base*		_base ;
	Berkeley			_bs ;
	sockaddr 			_ip ;
	event 			_ev ;
	
	void delegate()		read_cb, write_cb ;
	void delegate(short)	error_cb ;
	
	
	static if( isReferenceType!(T) ){
		alias T	This ;
	}else{
		alias T*	This ;
	}
	
	static This Init(event_base* base, evutil_socket_t fd, void* addr ) {
		assert(base !is null );
		This	p	= new T ;
		memcpy(&p._ip, addr,  sockaddr.sizeof );
		memcpy(&p._bs, &fd,  fd.sizeof );
		
		p._bs.family	= AddressFamily.INET;
		p._bs.type		= SocketType.STREAM ;
		p._bs.protocol	= ProtocolType.TCP;
		
		event_set(&p._ev, fd, EV_READ | EV_PERSIST | EV_WRITE , &Event_Cb,  p) ;
		
		return p ;
	}
	
	
	static extern(C) void  Event_Cb(evutil_socket_t fd, short ev, void* ctx ){
		This _self	=	cast(This) ctx ;
		Stdout.formatln("ev:{}", ev).flush;
		if( ev & EV_READ ){
			assert( (cast(int) _self.read_cb.ptr) !is 0 );
			_self.read_cb();
		}else if( ev & EV_READ ){
			assert( (cast(int) _self.write_cb.ptr) !is 0 );
			_self.write_cb();
		} else {
			_self.error_cb( ev );
		}
	}
	
}

struct UReq {
	static struct KeyVal {
		char[]	key ;
		char[]	val ;
	}
	alias UReq*	This ;
	char[]		path, url , qstring , fragment ;

	
	http_cb		on_message_begin	= &on_message_begin_fn ;
	http_data_cb	on_path			= &on_path_fn ;
	http_data_cb	on_query_string		= &on_query_string_fn ;
	http_data_cb	on_url			= &on_url_fn ;
	http_data_cb	on_fragment		= &on_fragment_fn ;
	http_data_cb	on_header_field		= &on_header_field_fn ;
	http_data_cb	on_header_value		= &on_header_value_fn ;
	http_cb		on_headers_complete	= &on_headers_complete_fn ;
	http_data_cb	on_body			= &on_body_fn ;
	http_cb		on_message_complete	= &on_message_complete_fn ;
	
	http_parser		parser ;
	bool			isHeadDone ;
	bool			isBodyDone ;
	
	KeyVal*[]		headers ;
	KeyVal*		_last ;
	bool			_last_was_value ;

	http_parser_settings* callbacks(){
		return cast(http_parser_settings*) ((cast(uint) this) + on_message_begin.offsetof );
	}
	
	bool parse(ubyte[] data) {
		int i	= http_parser_execute(&parser, callbacks, cast(char*) &data[0], data.length ) ;
		return i is data.length;
	}
	
	int isDone() {
		return parser.state ;
	}
	
	void Init(int type = 0 ) {
		http_parser_init(&parser, type);
		parser.data		= this ;
		isHeadDone		= false ;
		isBodyDone		= false ;
		headers.length	= 0 ;
		path.length		= 0 ;
		url.length		= 0 ;
		qstring.length	= 0 ;
		fragment.length	= 0 ;
		_last_was_value	= true ;
	}
	
	static extern(C) int on_message_begin_fn(http_parser* _p) {
		return 0 ;
	}
	
	static extern(C) int on_path_fn(http_parser* _p, char* da, size_t len){
		This ur	= cast(This) _p.data ;
		ur.path	= da[ 0 ..len].dup ;
		return 0 ;
	}

	static extern(C) int on_query_string_fn(http_parser* _p, char* da, size_t len) {
		This ur	= cast(This) _p.data ;
		ur.qstring	= da[ 0 ..len].dup ;
		return 0 ;
	}
	
	static extern(C) int on_url_fn(http_parser* _p, char* da, size_t len){
		This ur	= cast(This) _p.data ;
		ur.url		= da[ 0 ..len].dup ;
		return 0 ;
	}
	
	static extern(C) int on_fragment_fn(http_parser* _p, char* da, size_t len){
		This ur		= cast(This) _p.data ;
		ur.fragment		= da[ 0 ..len].dup ;
		return 0 ;
	}
	
	static extern(C) int on_header_field_fn(http_parser* _p, char* da, size_t len){
		This ur		= cast(This) _p.data ;
		if( ur._last_was_value ) {
			ur._last	= new KeyVal ;
			ur.headers	~= ur._last ;
			ur._last.key	= da[0..len].dup  ;
		} else {
			ur._last.key	= ( ur._last.key ~ da[0..len] ).dup  ;
		}
		ur._last_was_value 	= false ;
		return 0 ;
	}
	
	static extern(C) int on_header_value_fn(http_parser* _p, char* da, size_t len){
		This ur		= cast(This) _p.data ;
		if( !ur._last_was_value ) {
			ur._last.val	= da[0..len].dup  ;
		} else {
			ur._last.val	= ( ur._last.val ~ da[0..len] ) . dup  ;
		}
		ur._last_was_value 	= true ;
		return 0 ;
	}
	
	static extern(C) int on_body_fn(http_parser* _p, char* da, size_t len){
		This ur		= cast(This) _p.data ;
		ur.isBodyDone	= true  ;
		return 0 ;
	}
	
	static extern(C) int on_headers_complete_fn(http_parser* _p){
		This ur		= cast(This) _p.data ;
		ur.isHeadDone	= true  ;
		return 0 ;
	}
	
	static extern(C) int on_message_complete_fn(http_parser* _p){
		return 0 ;
	}
}

struct Serverlet {
	mixin IReq!(Serverlet) parent ;
	UReq	req ;
	
	void service() {
		write_cb	= &on_write ;
		Stdout.formatln("new req") ;
		req.Init ;
		read_cb		= &getRequest ;
		read_cb() ;

		
		/*
		auto bu	= new Array(1024, 1024 );
		evbuffer* input	= bufferevent_get_input(_bev);
		ubyte[1024] tmp;
		while( true ){
			int i	= evbuffer_remove(input, tmp.ptr, tmp.length );
			if( i is 0 || i > tmp.length ){
				break;
			}
			req.parse( tmp[0..i] );
			bu( tmp[0..i] );
		}
		
		Stdout.formatln("path: `{}`, qstring:`{}`  ", req.path , req.qstring );
		foreach( kv; req.headers){
			Stdout.formatln("{} : {} ", kv.key, kv.val );
		}
		Stdout.formatln("`{}`", cast(char[]) bu.slice);
		
		void baidu(Dns* p){
			
		}
		Dns.Query("google.com", &baidu);
		
		scope buh	= new Array(1024, 1024 );
		buh("HTTP/1.1 200 OK\r\n");
		buh("Content-Length: ")( Int.toString(bu.slice.length ) )("\r\n");
		buh("\r\n");
		
		evbuffer* output	= bufferevent_get_output(_bev)  ;
		
		evbuffer_add(output, buh.slice.ptr, buh.slice.length );
		evbuffer_add(output, bu.slice.ptr, bu.slice.length );
		*/
	}
	
	
	void getRequest() {
		ubyte[1024] tmp;
		while( true ){
			int i	= _bs.receive( tmp );
			if( i is 0 || i > tmp.length ){
				break;
			}
			req.parse( tmp[0..i] );
		}
		if( req.parser.state < 30  ) {
			return ;
		}
		Stdout.formatln("state:{}", req.parser.state).flush ;
		Stdout.formatln("path: `{}`, qstring:`{}`  ", req.path , req.qstring );
		foreach( kv; req.headers){
			Stdout.formatln("{} : {} ", kv.key, kv.val );
		}
	}
	
	
	
	void on_write() {
		Stdout.formatln("on_write\n\n\n\n") ;
	}
}

